/**
 *	\file AKMediator.m
 *  
 *  \section desc_sec Description
 *  Description
 *  
 *  \if copyright
 *  	Copyright 2008 Ben Chen. All rights reserved.
 *  \endif
 *  
 *  \section hist_sec Change History:
 *  	-	1/19/08		BC		Created this file
 *  
 *  <hr>
 *  \addtogroup AnimalKingdom
 *  @{
 */

#import "AKMediator.h"

#import "AKDrawing.h"

#import "AKPlugIn.h"

#define AKMediatorAddingPieces		@"addingPieces"
#define AKMediatorMovingPieces		@"movingPieces"
#define AKMediatorRemovingPieces	@"removingPieces"

@implementation AKMediator

- (id)initWithStateMaintainer:(id <AKStateMaintainer>)aStateMaintainer
					  players:(NSArray*)newPlayers
				 basicDrawing:(id <AKBasicDrawing>)aBasicDrawingObject 
#ifndef AKIPHONE
				soundProvider:(id <AKSoundProvider>)aSoundProvider 
			  soundController:(AKSoundController*)aSoundController
#endif
{
	self = [super init];
	
	if (self) {
		stateMaintainer		= [aStateMaintainer retain];
		players				= [newPlayers retain];
		basicDrawing		= [aBasicDrawingObject retain];
#ifndef AKIPHONE
		soundProvider		= [aSoundProvider retain];
		soundController		= [aSoundController retain];
#else
		canReceiveAlert		= NO;
#endif
		basicDrawing.delegate = self;
		
		canPerformObservingMethod = NO;
	}
	
	return self;
}

- (void)dealloc {
	[stateMaintainer	release];
	[players			release];
	[basicDrawing		release];
#ifndef AKIPHONE
	[soundProvider		release];
	[soundController	release];
#endif
	[super dealloc];
}

#pragma mark Helper Functions

- (void)performMoveForAllPlayers:(AKMove)newMove {
	for (id <AKPlayer> aPlayer in players) {
		if (![aPlayer performMove:newMove])
			[[NSException exceptionWithName:NSInternalInconsistencyException
									 reason:[NSString stringWithFormat:@"AKMediator: player:%@ couldn't perform move:%@", aPlayer, AKStringFromMove(newMove)] 
								   userInfo:nil] raise];
	}
}

- (id <AKPlayer>)playerForSide:(AKSideIndex)nextSide {
	id <AKPlayer> retVal = nil;
	for (id <AKPlayer> aPlayer in players) {
		if ([aPlayer side] == nextSide) {
			retVal = aPlayer;
			break;
		}
	}	
	return retVal;
}

- (void)backToInitialState {
	[basicDrawing removeAllPieces];
	[stateMaintainer backToInitialState];
	
	for (id <AKPlayer> aPlayer in players) {
		[aPlayer backToInitialState];
	}
	
	if ([stateMaintainer.createdPieces count]) {
		[basicDrawing beginTransaction:AKMediatorAddingPieces];
		
		for (NSValue *pieceValue in stateMaintainer.createdPieces) {
			AKPiece createdPiece;
			[pieceValue getValue:&createdPiece];
			[basicDrawing addPiece:createdPiece];
		}
		
		[basicDrawing commitTransaction];
	}
}

#define BestMove	@"bestMove"

#ifdef AKIPHONE
// Modal view delegate methods
- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)buttonIndex {
	if (canReceiveAlert) {
		canReceiveAlert = NO;
		if (anAlertView == alertView)
			[self start];
	}
}
#endif

- (void)play {
	NSArray *winners = [stateMaintainer winners];
	if ([winners count] != 0) {
		NSMutableString *msg = [NSMutableString string];
		
		if ([winners count] > 1) {
			[msg appendString:@"Draw!!!"];
		} else {
			[msg appendString:@"Winner(s):"];
			for (NSNumber *aWinner in winners) {
				[msg appendString:[NSString stringWithFormat:@" Side:%d", [aWinner unsignedIntValue]]];
			}
			[msg appendString:@"."];
		}
		
#ifndef AKIPHONE
		if (NSAlertDefaultReturn == NSRunAlertPanel(@"Game Over", msg, @"Play again", @"Quit", nil))
			[self start];
		else
			[NSApp terminate:self];
#else
		alertView = [[UIAlertView alloc] initWithTitle:@"Game Over" 
											   message:msg
											  delegate:self
									 cancelButtonTitle:@"Play again" 
									 otherButtonTitles:nil];
		canReceiveAlert = YES;
		[alertView show];
#endif
	} else {
		// If no winner yet
		AKSideIndex nextSide = [stateMaintainer nextPlayerSide];
		id <AKPlayer> nextPlayer = [self playerForSide:nextSide];
		if (nextPlayer) {
			canPerformObservingMethod = YES;
			[(id)nextPlayer addObserver:self forKeyPath:BestMove options:NSKeyValueObservingOptionNew context:(void*)nextPlayer];			
			[nextPlayer thinkNextMove];
		}
	}
}

- (void)observeValueForKeyPath:(NSString *)keyPath 
					  ofObject:(id)object
						change:(NSDictionary *)change
					   context:(void *)context {
	if (canPerformObservingMethod && [keyPath isEqualToString:BestMove]) {
		// Now the player has came up with a move
		
		canPerformObservingMethod = NO;
		[(id)context removeObserver:self forKeyPath:BestMove];	// context should be the object being observed
		
		// We get the new move by asking the player
		id <AKPlayer> nextPlayer = (id <AKPlayer>)object;
		AKMove nextMove = [nextPlayer bestMove];
		
		// Then we ask the state maintainer and all players (including the player that came up with this move) to perform this move
		if (![stateMaintainer performMove:nextMove]) {
			[[NSException exceptionWithName:NSInternalInconsistencyException
									 reason:[NSString stringWithFormat:@"AKMediator: can't perform move:%@", AKStringFromMove(nextMove)]
								   userInfo:nil] raise];
		}

		[self performMoveForAllPlayers:nextMove];
	
		// Then we present this move
		NSArray *createdPieces = stateMaintainer.createdPieces;
		
		[basicDrawing beginTransaction:AKMediatorAddingPieces];
		if (createdPieces && [createdPieces count]) {
			for (NSValue *pieceValue in createdPieces) {
				AKPiece createdPiece;
				[pieceValue getValue:&createdPiece];
				[basicDrawing addPiece:createdPiece];
				
#ifndef AKIPHONE
				[soundController playSound:[soundProvider soundForEvent:kAKEventPieceCreated piece:createdPiece info:nil] 
							  continuously:NO inBackground:NO];
#endif
			}
		}
		[basicDrawing commitTransaction];
		
		// when the transaction completes, the drawer will call transaction:didFinished:
	}
}

- (void)start {	
	[self backToInitialState];
#ifndef AKIPHONE
	[soundController playSound:[soundProvider soundForEvent:kAKEventGameStarted piece:AKZeroPiece info:nil] continuously:NO inBackground:NO];
#endif
	[self play];
}

#pragma mark Basic drawing's delegate methods

- (void)transaction:(NSString*)transactionName
		didFinished:(AKDrawing*)aDrawer {
	if ([transactionName isEqualToString:AKMediatorAddingPieces]) {
		[basicDrawing beginTransaction:AKMediatorMovingPieces];
		NSArray *movedPieces = stateMaintainer.movedPieces;
		if (movedPieces && [movedPieces count]) {
			for (NSValue *moveValue in movedPieces) {
				AKMove movedPiece;
				[moveValue getValue:&movedPiece];
				[basicDrawing movePiece:AKPieceInMove(movedPiece, YES) toCell:movedPiece.dstCell];
				
#ifndef AKIPHONE
				[soundController playSound:[soundProvider soundForEvent:kAKEventPieceMoved piece:AKPieceInMove(movedPiece, NO) info:nil] 
							  continuously:NO inBackground:NO];
#endif
			}
		}
		[basicDrawing commitTransaction];
	} else if ([transactionName isEqualToString:AKMediatorMovingPieces]) {
		[basicDrawing beginTransaction:AKMediatorRemovingPieces];
		NSArray *eliminatedPieces = stateMaintainer.eliminatedPieces;
		if (eliminatedPieces && [eliminatedPieces count]) {
			for (NSValue *pieceValue in eliminatedPieces) {
				AKPiece eliminatedPiece;
				[pieceValue getValue:&eliminatedPiece];
				[basicDrawing removePiece:eliminatedPiece];
				
#ifndef AKIPHONE
				[soundController playSound:[soundProvider soundForEvent:kAKEventPieceEliminated piece:eliminatedPiece info:nil] 
							  continuously:NO inBackground:NO];
#endif
			}
		}
		[basicDrawing commitTransaction];
	} else if ([transactionName isEqualToString:AKMediatorRemovingPieces]) {
		// All animations should be over when we got there
		[self play];
	}
}

@end

/** @} */